package net.xo68.coordinator.core.rpc.impl;


import io.protostuff.LinkedBuffer;
import io.protostuff.ProtostuffIOUtil;
import io.protostuff.Schema;
import io.protostuff.runtime.RuntimeSchema;
import net.xo68.coordinator.core.rpc.ObjectSerialize;
import net.xo68.coordinator.core.rpc.ObjectSerializeException;
import net.xo68.coordinator.core.util.ThrowUtil;

import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * 基于Protostuff的对象序列化实现
 * @author wuxie
 * @version 2020-9-29
 */
public class ProtostuffObjectSerializeImpl implements ObjectSerialize {

    private static final ConcurrentMap<String, Class<?>> classCache = new ConcurrentHashMap<>();

    @Override
    public <T> byte[] serialize(final T obj) throws ObjectSerializeException {
        final Schema<T> schema = RuntimeSchema.getSchema((Class<T>) obj.getClass());
        // Re-use (manage) this buffer to avoid allocating on every serialization
        LinkedBuffer buffer = LinkedBuffer.allocate(512);
        // ser
        final byte[] protostuff;
        try
        {
            protostuff = ProtostuffIOUtil.toByteArray(obj, schema, buffer);
        }
        finally
        {
            buffer.clear();
        }
        return protostuff;
    }

    @Override
    public <T> T deserialize(final byte[] data, String classOfT) throws ObjectSerializeException {
        Class<?> clazz = classCache.get(classOfT);
        if (clazz == null) {
            try {
                final Class<?> newClazz = Class.forName(classOfT);
                clazz = classCache.putIfAbsent(classOfT, newClazz);
                if (clazz == null) {
                    clazz = newClazz;
                }
            } catch (final Exception e) {
                ThrowUtil.throwException(e);
            }
        }
        return (T) deserialize(data,clazz);
    }

    @Override
    public <T> T deserialize(final byte[] data, Class<T> clazz) throws ObjectSerializeException {
        final Schema<T> schema = RuntimeSchema.getSchema(clazz);
        final T msg = schema.newMessage();
        ProtostuffIOUtil.mergeFrom(data, msg, schema);
        return msg;
    }
}
